#!/bin/sh

. /usr/share/libubox/jshn.sh

get_wireless_devices(){
	echo `ip link | awk 'BEGIN { FS=": "; } /^[[:digit:]]{2}/ { if(match($2, /wl/)) print $2 }'`
}

# wireless info dump. Expected output of wlctl: 
# SSID: "MyAP"
# Mode: Managed	RSSI: 0 dBm	SNR: 0 dB	noise: -80 dBm	Channel: 1
# BSSID: 00:22:07:1E:7F:E9	Capability: ESS ShortSlot RRM 
# Supported Rates: [ 6(b) 24 54 ]
# HT Capable:
# 	Chanspec: 2.4GHz channel 1 20MHz (0x1001)
# 	Primary channel: 1
# 	HT Capabilities: 
# 	Supported MCS : [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ]

wireless_devices(){
	WLDEVICES=`get_wireless_devices`
	json_init

	json_add_array "devices"; 
	for wdev in $WLDEVICES; do 
		DATA=$(wlctl -i $wdev assoc | awk 'BEGIN { 
			FS="[ \t]"; 
		} 
		{
			for(c = 0; c < NF; c++){
				if($c == "SSID:"){
					gsub(/"/, "", $(c+1)); 
					print "string|ssid=" $(c+1);
				}
				if($c == "Mode:") print "string|mode=" $(c+1); 
				if($c == "RSSI:") print "int|rssi=" $(c+1);
				if($c == "SNR:") print "int|snr=" $(c+1);
				if($c == "noise:") print "int|noise=" $(c+1);
				if($c == "Channel:") print "int|channel=" $(c+1);
				if($c == "BSSID:") print "string|bssid=" $(c+1);  
				if($c == "Rates:") {
					printf "string|rates="; 
					c+=2; 
					while($c != "]" && c < NF) {
						printf $(c); 
						c++; 
						if($c != "]") printf ","
					} 
					printf "\n"; 
				}
				if($c == "Chanspec:") print "string|frequency=" $(c+1);  
				if($c == "channel") print "int|channel=" $(c+1); 
				if($c == "channel:") print "int|primary_channel=" $(c+1) ;   
				if($c == "MCS") {
					printf "string|supported_channels="; 
					c+=3; 
					while($c != "]" && c < NF) {
						if($c != "[" && $c != "]") printf $(c); 
						if($c != "[" && $(c+1) != "]" && (c+1) != NF) printf ","
						c++; 
					} 
					printf "\n"; 
				}
			}
		}');
		
		json_add_object
		json_add_string "device" "$wdev"; 
		for field in $DATA; do 
			type=${field%%|*}
			nv=${field##*|}
			if [ "$type" == "string" ]; then json_add_string ${nv%=*} ${nv#*=}; fi
			if [ "$type" == "int" ]; then json_add_int ${nv%=*} ${nv#*=}; fi
		done
		json_close_object 
	done; 
	json_close_array
	json_dump
}

wireless_clients(){
	WLDEVICES=`get_wireless_devices`
	
	json_init
	json_add_array "clients"
	for wdev in $WLDEVICES; do
		CLIENTS=$(wlctl -i $wdev assoclist | cut -d' ' -f2); 
		BAND=$(wlctl -i $wdev band); 
		if [ "$BAND" == "b" ]; then BAND="2.4GHz"; else BAND="5GHz"; fi
		for cl in $CLIENTS; do
			local RSSI=$(wlctl -i "$wdev" rssi "$cl"); 
			local NOISE=$(wlctl -i "$wdev" noise); 
			local FLAGS="AUTHED"; 
			ARP=`cat /proc/net/arp | grep -i "$cl" | awk '/[[:digit:]]/ { print "ipaddr=" $1; }'`; 
			if [ "$ARP" == "" ]; then FLAGS="$FLAGS,NOIP"; else FLAGS="$FLAGS,UP"; fi
			json_add_object 
			json_add_string "macaddr" "$cl"
			json_add_string "device" "$wdev"
			json_add_string "band" "$BAND"
			json_add_string "flags" "$FLAGS"
			json_add_int "rssi" "$RSSI"
			json_add_int "noise" "$NOISE"
			json_add_string "hostname" $(grep -i "$cl" /var/dhcp.leases | awk '{print $4;}'); 
			for arp in $ARP; do 
				json_add_string ${arp%=*} ${arp#*=};
			done
			json_close_object
		done
	done
	json_close_array
	json_dump
}

wireless_radios(){
	RADIOS=`grep wifi-device /etc/config/wireless | awk -F\' '{print $2}'`
	
	json_init
	for radio in $RADIOS; do
		if [ "$(wlctl -i $radio apsta)" == "1" ]; then continue; fi
		json_add_object "$radio"
		FREQ=$(wlctl -i $radio assoc | awk -F' ' '/Chanspec/ { print $2; }'); 
		if [ "$FREQ" == "2.4GHz" ]; then
			json_add_array "hwmodes";
			json_add_string "" "auto"; 
			json_add_string "" "11b"; 
			json_add_string "" "11g"; 
			json_add_string "" "11bg"; 
			json_add_string "" "11n";
			json_close_array
			json_add_array "bwcaps"; 
			json_add_int "" 20
			json_add_int "" 40
			json_close_array
		elif [ "$FREQ" == "5GHz" ]; then
			json_add_array "hwmodes";
			# get deviceid of the device and then lookup if the device ac mode is supported (manually set in db) 
			devid=$(wlctl -i $radio revinfo | awk '/deviceid/{print substr($2,3);}'); 
			if [ $(db -q get hw.$devid.is_ac) == "1" ]; then
				json_add_string "" "11ac"; 
			fi
			json_add_string "" "auto"; 
			json_add_string "" "11a"; 
			json_add_string "" "11n";
			json_close_array
			json_add_array "bwcaps"; 
			json_add_int "" 20
			json_add_int "" 40
			json_add_int "" 80
			json_close_array
		fi
		json_add_array "channels";
		json_add_string "" "auto"
		for chan in $(wlctl -i $radio channels); do
			json_add_int "" "$chan"
		done
		json_close_array
		json_add_string "frequency" "$FREQ"
		json_close_object
	done
	json_dump
}

wireless_defaults(){
	json_init
	json_add_object "keys"; 
	json_add_string "wpa" $(db get hw.board.wpaKey)
	json_close_object
	json_dump
}

wireless_info(){
	WLDEVICES=`get_wireless_devices`
	json_init

	json_add_array "devices"; 
	for wdev in $WLDEVICES; do 
		DATA=$(wlctl -i $wdev assoc | awk 'BEGIN { 
			FS="[ \t]"; 
		} 
		{
			for(c = 0; c < NF; c++){
				if($c == "SSID:") {
					gsub(/"/, "", $(c+1)); 
					print "string|ssid=" $(c+1);
				}
				if($c == "Mode:") print "string|mode=" $(c+1); 
				if($c == "RSSI:") print "int|rssi=" $(c+1);
				if($c == "SNR:") print "int|snr=" $(c+1);
				if($c == "noise:") print "int|noise=" $(c+1);
				if($c == "Channel:") print "int|channel=" $(c+1);
				if($c == "BSSID:") print "string|bssid=" $(c+1);  
				if($c == "Rates:") {
					printf "string|rates="; 
					c+=2; 
					while($c != "]") {
						printf $(c); 
						c++; 
						if($c != "]") printf ","
					} 
					printf "\n"; 
				}
				if($c == "Chanspec:") print "string|frequency=" $(c+1);  
				if($c == "channel") print "int|channel=" $(c+1); 
				if($c == "channel:") print "int|primary_channel=" $(c+1) ;   
				if($c == "MCS") {
					printf "string|supported_channels="; 
					c+=3; 
					while($c != "]") {
						printf $(c); 
						if($c != "[" && $(c+1) != "]") printf ","
						c++; 
					} 
					printf "\n"; 
				}
			}
		}');
		
		json_add_object
			json_add_string "device" $wdev; 
		for field in $DATA; do 
			type=${field%%|*}
			nv=${field##*|}
			if [ "$type" == "string" ]; then json_add_string ${nv%=*} ${nv#*=}; fi
			if [ "$type" == "int" ]; then json_add_int ${nv%=*} ${nv#*=}; fi
		done
		json_close_object
	done; 
	json_close_array
	json_dump
}

SCANFILE="/var/run/juci.wifi.scanresults"; 

wireless_scan(){
	wlctl scan &> /dev/null
	sleep 1
	wlctl scanresults | mawk '
	/^SSID/{
		printf "\nssid=" substr($0, index($0, ":")+2) ";"; 
	}
	{
		for(c = 0; c <= NR; c++){
			if($c == "Mode:") printf "mode=\"" $(c+1) "\";"; 
			if($c == "RSSI:") printf "rssi=\"" $(c+1) "\";"; 
			if($c == "SNR:") printf "snr=\"" $(c+1) "\";"; 
			if($c == "noise:") printf "noise=\"" $(c+1) "\";"; 
			if($c == "Channel:") printf "channel=\"" $(c+1) "\";"; 
			if($c == "BSSID:") printf "bssid=\"" $(c+1) "\";"; 
			if($c == "multicast" && $(c+1) == "cipher:") printf "multicast_cipher=\"" $(c+2) "\";"; 
			if($c == "AKM") printf "cipher=\"" $(c+2) "\";"; 
			if($c == "Chanspec:") printf "frequency=\"" $(c+1) "\";";  
			if($c == "Primary" && $(c+1) == "channel:") printf "primary_channel=\"" $(c+2) "\";"; 
			if($c == "WPS:") printf "wps_version=\"" $(c+1) "\";"; 
		} 
	}' &> "$SCANFILE"; 
}

# SSID: "Inteno-F37E"
# Mode: Managed	RSSI: -79 dBm	SNR: 0 dB	noise: -88 dBm	Channel: 6
# BSSID: 00:22:07:4E:F3:7D	Capability: ESS WEP ShortSlot RRM 
# Supported Rates: [ 6(b) 24 54 ]
# RSN:
# 	multicast cipher: AES-CCMP
# 	unicast ciphers(1): AES-CCMP 
# 	AKM Suites(1): WPA-PSK 
# 	Capabilities(0x000c): No Pre-Auth, Pairwise, 16 PTK Replay Ctrs1 GTK Replay Ctr
# HT Capable:
# 	Chanspec: 2.4GHz channel 6 20MHz (0x1006)
# 	Primary channel: 6
# 	HT Capabilities: SGI20 
# 	Supported MCS : [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ]
# WPS: V2.0 Configured

wireless_scanresults(){
	json_init
	json_add_array "access_points" 
	IFS=$'\n'; 
	RESULTS=$(cat $SCANFILE); 
	for AP in $RESULTS; do
		eval "$AP"; 
		
		json_add_object ""
		json_add_string "ssid" "$ssid"; 
		json_add_int "rssi" "$rssi";
		json_add_int "snr" "$snr"; 
		json_add_int "noise" "$noise"; 
		json_add_int "channel" "$channel"; 
		json_add_string "bssid" "$bssid"; 
		json_add_string "multicast_cipher" "$multicast_cipher"; 
		json_add_string "cipher" "$cipher"; 
		json_add_string "frequency" "$frequency"; 
		json_add_int "channel" "$channel"; 
		json_add_int "primary_channel" "$primary_channel"; 
		json_add_string "wps_version" "$wps_version"; 
		json_close_object
	done
	json_close_array
	json_dump
}

case $1 in 
	.methods) 
		echo "devices,clients,radios,defaults,info,scan,scanresults"; 
		exit 0
		;;
	info) 
		wireless_info
		;;
	devices) 
		wireless_devices
		;;
	defaults) 
		wireless_defaults
		;;
	clients)
		wireless_clients
		;;
	radios)
		wireless_radios
		;;
	scan)
		wireless_scan "$2"
		;;
	scanresults)
		wireless_scanresults
		;;
esac
